{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "import os\n",
    "from glob import glob\n",
    "import shutil\n",
    "import sys\n",
    "import json\n",
    "import time\n",
    "import datetime\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt \n",
    "from PIL import Image\n",
    "%matplotlib inline\n",
    "# sys.path.append(\"’引用模块的地址'\") \n",
    "# 这两行代码解决 plt 中文显示的问题\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "#设定随机种子\n",
    "np.random.seed(0)\n",
    "# img_path\n",
    "def plt_img(img):\n",
    "    cv2.imwrite(\"out.jpg\", img)\n",
    "    img = Image.open(\"out.jpg\")\n",
    "    img = np.array(img)\n",
    "    plt.imshow(img)\n",
    "  #  plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# sys.path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "<ipython-input-19-6068bb33c938>:17: DeprecationWarning: an integer is required (got type numpy.float32).  Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python.\n",
      "  cv2.rectangle(img, (gt[0], gt[1]), (gt[2], gt[3]), (0,255,255), 1)\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "np.random.seed(0)\n",
    "# read image\n",
    "path = r\"D:\\work space\\git\\ImageProcessing100Wen\\Question_91_100\"\n",
    "img_path = os.path.join(path, 'imori_1.jpg')\n",
    "img = cv2.imread(img_path)\n",
    "\n",
    "H, W, C = img.shape\n",
    "\n",
    "# Grayscale\n",
    "gray = 0.2126 * img[..., 2] + 0.7152 * img[..., 1] + 0.0722 * img[..., 0]\n",
    "\n",
    "gt = np.array((47, 41, 129, 103), dtype=np.float32)\n",
    "\n",
    "cv2.rectangle(img, (gt[0], gt[1]), (gt[2], gt[3]), (0,255,255), 1)\n",
    "\n",
    "def iou(a, b):\n",
    "    area_a = (a[2] - a[0]) * (a[3] - a[1])\n",
    "    area_b = (b[2] - b[0]) * (b[3] - b[1])\n",
    "    iou_x1 = np.maximum(a[0], b[0])\n",
    "    iou_y1 = np.maximum(a[1], b[1])\n",
    "    iou_x2 = np.minimum(a[2], b[2])\n",
    "    iou_y2 = np.minimum(a[3], b[3])\n",
    "    iou_w = max(iou_x2 - iou_x1, 0)\n",
    "    iou_h = max(iou_y2 - iou_y1, 0)\n",
    "    area_iou = iou_w * iou_h\n",
    "    iou = area_iou / (area_a + area_b - area_iou)\n",
    "    return iou\n",
    "\n",
    "\n",
    "def hog(gray):\n",
    "    h, w = gray.shape\n",
    "    # Magnitude and gradient\n",
    "    gray = np.pad(gray, (1, 1), 'edge')\n",
    "\n",
    "    gx = gray[1:h+1, 2:] - gray[1:h+1, :w]\n",
    "    gy = gray[2:, 1:w+1] - gray[:h, 1:w+1]\n",
    "    gx[gx == 0] = 0.000001\n",
    "\n",
    "    mag = np.sqrt(gx ** 2 + gy ** 2)\n",
    "    gra = np.arctan(gy / gx)\n",
    "    gra[gra<0] = np.pi / 2 + gra[gra < 0] + np.pi / 2\n",
    "\n",
    "    # Gradient histogram\n",
    "    gra_n = np.zeros_like(gra, dtype=np.int)\n",
    "\n",
    "    d = np.pi / 9\n",
    "    for i in range(9):\n",
    "        gra_n[np.where((gra >= d * i) & (gra <= d * (i+1)))] = i\n",
    "\n",
    "    N = 8\n",
    "    HH = h // N\n",
    "    HW = w // N\n",
    "    Hist = np.zeros((HH, HW, 9), dtype=np.float32)\n",
    "    for y in range(HH):\n",
    "        for x in range(HW):\n",
    "            for j in range(N):\n",
    "                for i in range(N):\n",
    "                    Hist[y, x, gra_n[y*4+j, x*4+i]] += mag[y*4+j, x*4+i]\n",
    "                \n",
    "    ## Normalization\n",
    "    C = 3\n",
    "    eps = 1\n",
    "    for y in range(HH):\n",
    "        for x in range(HW):\n",
    "            #for i in range(9):\n",
    "            Hist[y, x] /= np.sqrt(np.sum(Hist[max(y-1,0):min(y+2, HH), max(x-1,0):min(x+2, HW)] ** 2) + eps)\n",
    "\n",
    "    return Hist\n",
    "\n",
    "def resize(img, h, w):\n",
    "    _h, _w  = img.shape\n",
    "    ah = 1. * h / _h\n",
    "    aw = 1. * w / _w\n",
    "    y = np.arange(h).repeat(w).reshape(w, -1)\n",
    "    x = np.tile(np.arange(w), (h, 1))\n",
    "    y = (y / ah)\n",
    "    x = (x / aw)\n",
    "\n",
    "    ix = np.floor(x).astype(np.int32)\n",
    "    iy = np.floor(y).astype(np.int32)\n",
    "    ix = np.minimum(ix, _w-2)\n",
    "    iy = np.minimum(iy, _h-2)\n",
    "\n",
    "    dx = x - ix\n",
    "    dy = y - iy\n",
    "    \n",
    "    out = (1-dx) * (1-dy) * img[iy, ix] + dx * (1 - dy) * img[iy, ix+1] + (1 - dx) * dy * img[iy+1, ix] + dx * dy * img[iy+1, ix+1]\n",
    "    out[out>255] = 255\n",
    "\n",
    "    return out\n",
    "\n",
    "\n",
    "class NN:\n",
    "    def __init__(self, ind=2, w=64, w2=64, outd=1, lr=0.1):\n",
    "        self.w1 = np.random.normal(0, 1, [ind, w])\n",
    "        self.b1 = np.random.normal(0, 1, [w])\n",
    "        self.w2 = np.random.normal(0, 1, [w, w2])\n",
    "        self.b2 = np.random.normal(0, 1, [w2])\n",
    "        self.wout = np.random.normal(0, 1, [w2, outd])\n",
    "        self.bout = np.random.normal(0, 1, [outd])\n",
    "        self.lr = lr\n",
    "\n",
    "    def forward(self, x):\n",
    "        self.z1 = x\n",
    "        self.z2 = sigmoid(np.dot(self.z1, self.w1) + self.b1)\n",
    "        self.z3 = sigmoid(np.dot(self.z2, self.w2) + self.b2)\n",
    "        self.out = sigmoid(np.dot(self.z3, self.wout) + self.bout)\n",
    "        return self.out\n",
    "\n",
    "    def train(self, x, t):\n",
    "        # backpropagation output layer\n",
    "        #En = t * np.log(self.out) + (1-t) * np.log(1-self.out)\n",
    "        En = (self.out - t) * self.out * (1 - self.out)\n",
    "        grad_wout = np.dot(self.z3.T, En)\n",
    "        grad_bout = np.dot(np.ones([En.shape[0]]), En)\n",
    "        self.wout -= self.lr * grad_wout\n",
    "        self.bout -= self.lr * grad_bout\n",
    "\n",
    "        # backpropagation inter layer\n",
    "        grad_u2 = np.dot(En, self.wout.T) * self.z3 * (1 - self.z3)\n",
    "        grad_w2 = np.dot(self.z2.T, grad_u2)\n",
    "        grad_b2 = np.dot(np.ones([grad_u2.shape[0]]), grad_u2)\n",
    "        self.w2 -= self.lr * grad_w2\n",
    "        self.b2 -= self.lr * grad_b2\n",
    "        \n",
    "        grad_u1 = np.dot(grad_u2, self.w2.T) * self.z2 * (1 - self.z2)\n",
    "        grad_w1 = np.dot(self.z1.T, grad_u1)\n",
    "        grad_b1 = np.dot(np.ones([grad_u1.shape[0]]), grad_u1)\n",
    "        self.w1 -= self.lr * grad_w1\n",
    "        self.b1 -= self.lr * grad_b1\n",
    "\n",
    "def sigmoid(x):\n",
    "    return 1. / (1. + np.exp(-x))\n",
    "\n",
    "\n",
    "# crop and create database\n",
    "\n",
    "Crop_num = 200\n",
    "L = 60\n",
    "H_size = 32\n",
    "F_n = ((H_size // 8) ** 2) * 9\n",
    "\n",
    "db = np.zeros((Crop_num, F_n+1))\n",
    "\n",
    "for i in range(Crop_num):\n",
    "    x1 = np.random.randint(W-L)\n",
    "    y1 = np.random.randint(H-L)\n",
    "    x2 = x1 + L\n",
    "    y2 = y1 + L\n",
    "    crop = np.array((x1, y1, x2, y2))\n",
    "\n",
    "    _iou = iou(gt, crop)\n",
    "\n",
    "    if _iou >= 0.5:\n",
    "        cv2.rectangle(img, (x1, y1), (x2, y2), (0,0,255), 1)\n",
    "        label = 1\n",
    "    else:\n",
    "        cv2.rectangle(img, (x1, y1), (x2, y2), (255,0,0), 1)\n",
    "        label = 0\n",
    "\n",
    "    crop_area = gray[y1:y2, x1:x2]\n",
    "    crop_area = resize(crop_area, H_size, H_size)\n",
    "    _hog = hog(crop_area)\n",
    "    \n",
    "    db[i, :F_n] = _hog.ravel()\n",
    "    db[i, -1] = label\n",
    "\n",
    "\n",
    "## training neural network\n",
    "nn = NN(ind=F_n, lr=0.01)\n",
    "for i in range(10000):\n",
    "    nn.forward(db[:, :F_n])\n",
    "    nn.train(db[:, :F_n], db[:, -1][..., None])\n",
    "\n",
    "\n",
    "# read detect target image\n",
    "img_path = os.path.join(path, 'imori_many.jpg')\n",
    "img2 = cv2.imread(img_path)\n",
    "H2, W2, C2 = img2.shape\n",
    "\n",
    "# Grayscale\n",
    "gray2 = 0.2126 * img2[..., 2] + 0.7152 * img2[..., 1] + 0.0722 * img2[..., 0]\n",
    "\n",
    "# [h, w]\n",
    "recs = np.array(((42, 42), (56, 56), (70, 70)), dtype=np.float32)\n",
    "\n",
    "detects = np.ndarray((0, 5), dtype=np.float32)\n",
    "\n",
    "# sliding window\n",
    "for y in range(0, H2, 4):\n",
    "    for x in range(0, W2, 4):\n",
    "        for rec in recs:\n",
    "            dh = int(rec[0] // 2)\n",
    "            dw = int(rec[1] // 2)\n",
    "            x1 = max(x-dw, 0)\n",
    "            x2 = min(x+dw, W2)\n",
    "            y1 = max(y-dh, 0)\n",
    "            y2 = min(y+dh, H2)\n",
    "            region = gray2[max(y-dh,0):min(y+dh,H2), max(x-dw,0):min(x+dw,W2)]\n",
    "            region = resize(region, H_size, H_size)\n",
    "            region_hog = hog(region).ravel()\n",
    "\n",
    "            score = nn.forward(region_hog)\n",
    "            if score >= 0.7:\n",
    "                #cv2.rectangle(img2, (x1, y1), (x2, y2), (0,0,255), 1)\n",
    "                detects = np.vstack((detects, np.array((x1, y1, x2, y2, score))))\n",
    "\n",
    "\n",
    "# Non-maximum suppression\n",
    "def nms(_bboxes, iou_th=0.5, select_num=None, prob_th=None):\n",
    "    #\n",
    "    # Non Maximum Suppression\n",
    "    #\n",
    "    # Argument\n",
    "    #  bboxes(Nx5) ... [bbox-num, 5(leftTopX,leftTopY,w,h, score)]\n",
    "    #  iou_th([float]) ... threshold for iou between bboxes.\n",
    "    #  select_num([int]) ... max number for choice bboxes. If None, this is unvalid.\n",
    "    #  prob_th([float]) ... probability threshold to choice. If None, this is unvalid.\n",
    "    # Return\n",
    "    #  inds ... choced indices for bboxes\n",
    "    #\n",
    "\n",
    "    bboxes = _bboxes.copy()\n",
    "    \n",
    "    bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]\n",
    "    bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]\n",
    "    \n",
    "    # Sort by bbox's score. High -> Low\n",
    "    sort_inds = np.argsort(bboxes[:, -1])[::-1]\n",
    "\n",
    "    processed_bbox_ind = []\n",
    "    return_inds = []\n",
    "\n",
    "    unselected_inds = sort_inds.copy()\n",
    "    \n",
    "    while len(unselected_inds) > 0:\n",
    "        process_bboxes = bboxes[unselected_inds]\n",
    "        argmax_score_ind = np.argmax(process_bboxes[::, -1])\n",
    "        max_score_ind = unselected_inds[argmax_score_ind]\n",
    "        return_inds += [max_score_ind]\n",
    "        unselected_inds = np.delete(unselected_inds, argmax_score_ind)\n",
    "\n",
    "        base_bbox = bboxes[max_score_ind]\n",
    "        compare_bboxes = bboxes[unselected_inds]\n",
    "        \n",
    "        base_x1 = base_bbox[0]\n",
    "        base_y1 = base_bbox[1]\n",
    "        base_x2 = base_bbox[2] + base_x1\n",
    "        base_y2 = base_bbox[3] + base_y1\n",
    "        base_w = np.maximum(base_bbox[2], 0)\n",
    "        base_h = np.maximum(base_bbox[3], 0)\n",
    "        base_area = base_w * base_h\n",
    "\n",
    "        # compute iou-area between base bbox and other bboxes\n",
    "        iou_x1 = np.maximum(base_x1, compare_bboxes[:, 0])\n",
    "        iou_y1 = np.maximum(base_y1, compare_bboxes[:, 1])\n",
    "        iou_x2 = np.minimum(base_x2, compare_bboxes[:, 2] + compare_bboxes[:, 0])\n",
    "        iou_y2 = np.minimum(base_y2, compare_bboxes[:, 3] + compare_bboxes[:, 1])\n",
    "        iou_w = np.maximum(iou_x2 - iou_x1, 0)\n",
    "        iou_h = np.maximum(iou_y2 - iou_y1, 0)\n",
    "        iou_area = iou_w * iou_h\n",
    "\n",
    "        compare_w = np.maximum(compare_bboxes[:, 2], 0)\n",
    "        compare_h = np.maximum(compare_bboxes[:, 3], 0)\n",
    "        compare_area = compare_w * compare_h\n",
    "\n",
    "        # bbox's index which iou ratio over threshold is excluded\n",
    "        all_area = compare_area + base_area - iou_area\n",
    "        iou_ratio = np.zeros((len(unselected_inds)))\n",
    "        iou_ratio[all_area < 0.9] = 0.\n",
    "        _ind = all_area >= 0.9\n",
    "        iou_ratio[_ind] = iou_area[_ind] / all_area[_ind]\n",
    "        \n",
    "        unselected_inds = np.delete(unselected_inds, np.where(iou_ratio >= iou_th)[0])\n",
    "\n",
    "    if prob_th is not None:\n",
    "        preds = bboxes[return_inds][:, -1]\n",
    "        return_inds = np.array(return_inds)[np.where(preds >= prob_th)[0]].tolist()\n",
    "        \n",
    "    # pick bbox's index by defined number with higher score\n",
    "    if select_num is not None:\n",
    "        return_inds = return_inds[:select_num]\n",
    "\n",
    "    return return_inds\n",
    "\n",
    "\n",
    "detects = detects[nms(detects, iou_th=0.25)]\n",
    "\n",
    "\n",
    "# Evaluation\n",
    "\n",
    "# [x1, y1, x2, y2]\n",
    "GT = np.array(((27, 48, 95, 110), (101, 75, 171, 138)), dtype=np.float32)\n",
    "\n",
    "## Recall, Precision, F-score\n",
    "iou_th = 0.5\n",
    "\n",
    "Rs = np.zeros((len(GT)))\n",
    "Ps = np.zeros((len(detects)))\n",
    "\n",
    "for i, g in enumerate(GT):\n",
    "    iou_x1 = np.maximum(g[0], detects[:, 0])\n",
    "    iou_y1 = np.maximum(g[1], detects[:, 1])\n",
    "    iou_x2 = np.minimum(g[2], detects[:, 2])\n",
    "    iou_y2 = np.minimum(g[3], detects[:, 3])\n",
    "    iou_w = np.maximum(0, iou_x2 - iou_x1)\n",
    "    iou_h = np.maximum(0, iou_y2 - iou_y1)\n",
    "    iou_area = iou_w * iou_h\n",
    "    g_area = (g[2] - g[0]) * (g[3] - g[1])\n",
    "    d_area = (detects[:, 2] - detects[:, 0]) * (detects[:, 3] - detects[:, 1])\n",
    "    ious = iou_area / (g_area + d_area - iou_area)\n",
    "    \n",
    "    Rs[i] = 1 if len(np.where(ious >= iou_th)[0]) > 0 else 0\n",
    "    Ps[ious >= iou_th] = 1\n",
    "    \n",
    "\n",
    "R = np.sum(Rs) / len(Rs)\n",
    "P = np.sum(Ps) / len(Ps)\n",
    "F = (2 * P * R) / (P + R) \n",
    "\n",
    "print(\"Recall >> {:.2f} ({} / {})\".format(R, np.sum(Rs), len(Rs)))\n",
    "print(\"Precision >> {:.2f} ({} / {})\".format(P, np.sum(Ps), len(Ps)))\n",
    "print(\"F-score >> \", F)\n",
    "\n",
    "## mAP\n",
    "mAP = 0.\n",
    "for i in range(len(detects)):\n",
    "    mAP += np.sum(Ps[:i]) / (i + 1) * Ps[i]\n",
    "mAP /= np.sum(Ps)\n",
    "\n",
    "print(\"mAP >>\", mAP)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Display\n",
    "for i in range(len(detects)):\n",
    "    v = list(map(int, detects[i, :4]))\n",
    "    if Ps[i] > 0:\n",
    "        cv2.rectangle(img2, (v[0], v[1]), (v[2], v[3]), (0,0,255), 1)\n",
    "    else:\n",
    "        cv2.rectangle(img2, (v[0], v[1]), (v[2], v[3]), (255,0,0), 1)\n",
    "    cv2.putText(img2, \"{:.2f}\".format(detects[i, -1][0]), (v[0], v[1]+9),\n",
    "                cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255,0,255), 1)\n",
    "\n",
    "for g in GT:\n",
    "    cv2.rectangle(img2, (g[0], g[1]), (g[2], g[3]), (0,255,0), 1)\n",
    "plt_img(img2)\n",
    "# cv2.imwrite(\"out.jpg\", img2)\n",
    "# cv2.imshow(\"result\", img2)\n",
    "# cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
